
import { IPiece, Result } from "@/third/chinese-chess/src";
import { CCTeam, CCBoard, CCPieceType, CCPiece } from "@/third/chinese-chess/src/demo/chinese_chess";
import { PieceNode } from "./PieceNode";

export class ChineseChessNode {
  team = CCTeam.Red;
  x = 1;
  y = 1;
  board = new CCBoard();
  bg_canvas?: HTMLCanvasElement | null;
  bg_ctx?: CanvasRenderingContext2D | null;
  fg_canvas?: HTMLCanvasElement | null;
  fg_ctx?: CanvasRenderingContext2D | null;
  cell_size = 50;
  cavans_scale: number = 1;
  get line_gap() {
    return Math.round(this.cell_size / 10);
  }
  get hash_gap() {
    return Math.round(this.cell_size / 5);
  }
  get bw() { return this.board?.width || 0; }
  get bh() { return this.board?.height || 0; }
  get width(): number {
    return (0.2 + this.bw) * this.cell_size;
  }
  get height(): number {
    return (0.2 + this.bh) * this.cell_size;
  }
  init(
    bg_canvas: HTMLCanvasElement | null,
    fg_canvas: HTMLCanvasElement | null
  ) {
    window.removeEventListener('mousemove', this.on_mousemove);
    this.fg_canvas?.removeEventListener('pointermove', this.on_pointermove);
    this.fg_canvas?.removeEventListener('pointerdown', this.on_pointerdown);
    this.bg_canvas = bg_canvas;
    this.fg_canvas = fg_canvas;
    this.bg_ctx = bg_canvas?.getContext('2d');
    this.fg_ctx = fg_canvas?.getContext('2d');
    window.addEventListener('mousemove', this.on_mousemove);
    this.fg_canvas?.addEventListener('pointermove', this.on_pointermove);
    this.fg_canvas?.addEventListener('pointerdown', this.on_pointerdown);
  }
  hover?: IPiece<CCTeam, CCPieceType> | null = null;
  picked?: IPiece<CCTeam, CCPieceType> | null = null;
  protected _mouse_x = Number.MAX_SAFE_INTEGER
  protected _mouse_y = Number.MAX_SAFE_INTEGER
  on_mousemove = (e: MouseEvent) => {
    if (e.target === this.fg_canvas) {
      this._mouse_x = e.offsetX
      this._mouse_y = e.offsetY
    } else {
      this._mouse_x = Number.MAX_SAFE_INTEGER
      this._mouse_y = Number.MAX_SAFE_INTEGER
    }
  }
  on_pointermove = (e: PointerEvent) => {
    let hover: typeof this.hover;
    do {
      const cbw = (0.2 + this.bw) * this.cell_size;
      const cbh = (0.2 + this.bh) * this.cell_size;
      if (e.offsetX < this.x || e.offsetY < this.y || e.offsetX > this.x + cbw || e.offsetY > this.y + cbh) {
        hover = null;
        break;
      }
      const [a, b] = this.get_b_xy(e.offsetX, e.offsetY);
      const bx = Math.round(a);
      const by = Math.round(b);
      if (Math.pow(bx - a, 2) + Math.pow(by - b, 2) > 0.20) {
        hover = null;
        break;
      }
      const piece = this.board.get_piece_by_xy(bx, by);
      hover = (this.board.acting_player?.team === piece?.team) ? piece : void 0;
    } while (0);

    if (this.hover === hover) return;
    this.hover = hover;
  };
  on_pointerdown = (e: PointerEvent) => {
    let point_at: typeof this.picked;
    do {
      const cbw = (0.2 + this.bw) * this.cell_size;
      const cbh = (0.2 + this.bh) * this.cell_size;
      if (e.offsetX < this.x || e.offsetY < this.y || e.offsetX > this.x + cbw || e.offsetY > this.y + cbh) {
        point_at = null;
        break;
      }
      const [a, b] = this.get_b_xy(e.offsetX, e.offsetY);
      const bx = Math.round(a);
      const by = Math.round(b);
      if (Math.pow(bx - a, 2) + Math.pow(by - b, 2) > 0.20) {
        point_at = null;
        break;
      }
      const piece = this.board.get_piece_by_xy(bx, by);
      point_at = (this.board.acting_player?.team === piece?.team) ? piece : void 0;
      point_at = this.picked === point_at ? null : point_at;
    } while (0);

    if (this.picked === point_at) return;

    if (!this.picked) {
      this.picked = point_at;
    } else {
      const [a, b] = this.get_b_xy(e.offsetX, e.offsetY);
      const bx = Math.round(a);
      const by = Math.round(b);
      const result = this.board.move_piece(this.picked, bx, by);
      if (result === Result.Resolve) {
        this.board.acting_player = this.board.next_acting_player;
        this.picked = null;
      } else {
        this.picked = point_at;
      }
    }
  };
  private board_dirty = true;
  draw(delta: number, time: number) {
    if (this.board_dirty) {
      this.draw_board(delta, time);
      this.board_dirty = false;
    }

    this.draw_pieces(delta, time);
    if (
      this._mouse_x < Number.MAX_SAFE_INTEGER && this._mouse_y < Number.MAX_SAFE_INTEGER && this.fg_ctx
    ) {
      this.fg_ctx.textAlign = 'left'
      this.fg_ctx.textBaseline = 'top';
      const ox = (window.devicePixelRatio || 1) * 15
      const oy = (window.devicePixelRatio || 1) * 15
      switch (this.board.acting_player?.team) {
        case CCTeam.Blk: {
          this.fg_ctx.fillStyle = 'black'
          this.fg_ctx.fillText("黑", this._mouse_x + ox, this._mouse_y + oy)
          break;
        }
        case CCTeam.Red: {
          this.fg_ctx.fillStyle = 'red'
          this.fg_ctx.fillText("红", this._mouse_x + ox, this._mouse_y + oy)
          break;
        }
      }
    }
  }
  release() {
    this.fg_canvas?.removeEventListener('pointermove', this.on_pointerdown);
    this.fg_canvas?.removeEventListener('pointerdown', this.on_pointermove);
  }
  get_c_xy(b_x: number, b_y: number) {
    if (this.team === CCTeam.Red)
      return [
        this.x + (b_x + 0.6) * this.cell_size,
        this.y + (b_y + 0.6) * this.cell_size
      ] as const;
    else
      return [
        this.x + (8 - b_x + 0.6) * this.cell_size,
        this.y + (9 - b_y + 0.6) * this.cell_size
      ] as const;
  }
  get_b_xy(c_x: number, c_y: number) {
    if (this.team === CCTeam.Red)
      return [
        (c_x - this.x) / this.cell_size - 0.6,
        (c_y - this.y) / this.cell_size - 0.6
      ] as const;
    else
      return [
        8 - (c_x - this.x) / this.cell_size + 0.6,
        9 - (c_y - this.y) / this.cell_size + 0.6
      ] as const;
  }
  private _piece_node_map = new Map<CCPiece, PieceNode>();
  draw_board(delta: number, time: number) {
    const { bg_canvas, bg_ctx, cell_size, x: ox, y: oy, line_gap, bw, bh, hash_gap } = this;
    if (!bg_canvas || !bg_ctx) return;
    bg_canvas.width = bg_canvas.width;
    bg_canvas.height = bg_canvas.height;
    bg_ctx.scale(this.cavans_scale, this.cavans_scale);
    bg_ctx.imageSmoothingEnabled = true;
    bg_ctx.imageSmoothingQuality = 'high';
    bg_ctx.fillStyle = 'white';
    bg_ctx.strokeStyle = 'black';
    bg_ctx.lineWidth = 1;
    bg_ctx.rect(ox, oy, this.width, this.height);
    bg_ctx.rect(
      ox + 0.6 * cell_size - line_gap,
      oy + 0.6 * cell_size - line_gap,
      (bw - 1) * cell_size + line_gap * 2,
      (bh - 1) * cell_size + line_gap * 2
    );
    bg_ctx.stroke();
    const draw_line = (x1: number, y1: number, x2: number, y2: number) => {
      const [a, b] = this.get_c_xy(x1, y1);
      const [c, d] = this.get_c_xy(x2, y2);
      const od = bg_ctx.lineWidth === 1 ? -0.5 : 0;
      bg_ctx.moveTo(a - od, b - od);
      bg_ctx.lineTo(c - od, d - od);
      bg_ctx.stroke();
    };
    for (let i = 0; i < bh; ++i) {
      draw_line(0, i, bw - 1, i);
    }
    for (let i = 0; i < bw; ++i) {
      if (i === 0 || i === bw - 1) {
        draw_line(i, 0, i, bh - 1);
      } else {
        draw_line(i, 0, i, 4);
        draw_line(i, 5, i, bh - 1);
      }
    }

    const draw_hash = (x: number, y: number) => {
      const [a, b] = this.get_c_xy(x, y);
      if (x > 0) {
        bg_ctx.moveTo(a - line_gap, b - line_gap - hash_gap);
        bg_ctx.lineTo(a - line_gap, b - line_gap);
        bg_ctx.lineTo(a - line_gap - hash_gap, b - line_gap);
        bg_ctx.stroke();
        bg_ctx.moveTo(a - line_gap, b + line_gap + hash_gap);
        bg_ctx.lineTo(a - line_gap, b + line_gap);
        bg_ctx.lineTo(a - line_gap - hash_gap, b + line_gap);
        bg_ctx.stroke();
      }
      if (x < 8) {
        bg_ctx.moveTo(a + line_gap, b - line_gap - hash_gap);
        bg_ctx.lineTo(a + line_gap, b - line_gap);
        bg_ctx.lineTo(a + line_gap + hash_gap, b - line_gap);
        bg_ctx.stroke();
        bg_ctx.moveTo(a + line_gap, b + line_gap + hash_gap);
        bg_ctx.lineTo(a + line_gap, b + line_gap);
        bg_ctx.lineTo(a + line_gap + hash_gap, b + line_gap);
        bg_ctx.stroke();
      }
    };
    for (let i = 0; i <= 8; i += 2) {
      draw_hash(i, 3);
      draw_hash(i, 6);
    }
    const [a, b] = this.get_c_xy(2, 4.5);
    const [c, d] = this.get_c_xy(6, 4.5);
    bg_ctx.fillStyle = 'gray';
    bg_ctx.font = `bold ${cell_size * 0.55}px serif`;
    bg_ctx.textAlign = 'center';
    bg_ctx.textBaseline = 'middle';
    bg_ctx.fillText("楚 河", a, b);
    bg_ctx.rotate(Math.PI);
    bg_ctx.fillText("汉 界", -c, -d);
    bg_ctx.rotate(Math.PI);
    bg_ctx.lineWidth = 1;
    draw_line(3, 0, 5, 2);
    draw_line(3, 7, 5, 9);
    draw_line(5, 0, 3, 2);
    draw_line(5, 7, 3, 9);
  }

  draw_pieces(delta: number, time: number) {
    const { fg_canvas, fg_ctx, board } = this;
    if (!fg_canvas || !fg_ctx || !board) return;
    fg_canvas.width = fg_canvas.width;
    fg_canvas.height = fg_canvas.height;
    fg_ctx.scale(this.cavans_scale, this.cavans_scale);
    fg_ctx.imageSmoothingEnabled = true;
    fg_ctx.imageSmoothingQuality = 'high';

    for (const p of board.pieces) {
      if (!p) continue;
      let n = this._piece_node_map.get(p);
      if (!n) this._piece_node_map.set(p, n = new PieceNode(this, p));
    }
    const piece_node_arr = Array.from(this._piece_node_map.values()).sort((a, b) => a.z - b.z);
    for (const n of piece_node_arr) {
      if (n.dead) this._piece_node_map.delete(n.piece);
      n.render(delta, time);
    }
  }
}
